
#include "FileManager.h"

#include <tuple>

#include <QUuid>

#include <Assert.h>
#include <QDateTime>
#include <AliOSSAdapter.h>

#include "Store\TempUploadsTable.h"

using namespace std;
using namespace TCServer;
using namespace TCServer::Store;
using namespace TCServer::Manager;
using namespace AliOSS;

namespace TCServer
{
	namespace Manager
	{
		FileManager::FileManager()
			:adminAdapter("tcres", "L9LT1rmDHa7P6HTF")
		{
		}

		TCServer::Manager::FileManager::PrepareResult FileManager::prepareUpload(const QString &ip, quint64 userId, const QString &md5inbase64, const QString &suffix)
        {
			auto it = suffixes.lowerBound(suffix);
			if (it.key() == suffix)
			{
				// content type
				QString contentType = it.value();

				// oss headers : uploadtime, uploader
				auto currentDatetime = QDateTime::currentDateTime();
				QMap<QString, QByteArray> ossHeaders = QMap<QString, QByteArray>({
					{ "x-oss-meta-uploader", QString::number(userId).toUtf8() },
					{ "x-oss-meta-uploaderip", ip.toUtf8() },
					{ "x-oss-meta-uploadtime", currentDatetime.toString("yyyy-MM-dd hh:mm:ss").toUtf8() },
				});

				// file path
                auto path = QString("temp/%1/%2.%3")
                    .arg(currentDatetime.toString("yyyy-MM-dd"))
                    .arg(QUuid::createUuid().toString())
                    .arg(suffix);

				// datetime string
				auto fixedDatetime = currentDatetime.addSecs(-60 * 14);
				auto datetimestr = AliOSS::AliOSSAdapter::formatDatetime(fixedDatetime.toUTC());

				// generate signature
				auto signature = userKeyGen.once(QStringLiteral("PUT"), datetimestr, path, md5inbase64, ossHeaders, contentType);

// 				Store::TempUploadsTable::insert(path);
                return FileManager::PrepareResult {
                    true,
                    path,
                    datetimestr,
                    signature,
                    contentType,
                    ossHeaders
                };
            }
            return FileManager::PrepareResult {
                false
            };
		}

		void FileManager::removeUrlAsync(const QString &url)
		{
			QString path = url2path(url);
			auto removeRequest = adminAdapter.prepareRemove(path);

			auto dtstr = AliOSSAdapter::formatDatetime(QDateTime::currentDateTimeUtc());
			auto signature = adminKeyGen.once(
				QStringLiteral("DELETE"),
				dtstr,
				path);
			removeRequest->setNeededInformation(dtstr, signature);
			removeRequest->doRemove();

            QObject::connect(removeRequest, &AliOSSRemoveRequest::uploadFailed,
                [url]()
            {
                qWarning() << "FileManager::removeUrlAsync(" << url << ")failed : " << endl;
            });
			QObject::connect(removeRequest, &AliOSSRemoveRequest::uploadError,
				[url](QNetworkReply::NetworkError error)
			{
				qWarning() << "FileManager::removeUrlAsync(" << url <<  "), error : " << error << endl;
			});

			// auto-delete
			QObject::connect(removeRequest, &AliOSSRemoveRequest::uploadFinished,
				removeRequest, &AliOSSRemoveRequest::deleteLater);
			QObject::connect(removeRequest, &AliOSSRemoveRequest::uploadFailed,
				removeRequest, &AliOSSRemoveRequest::deleteLater);
			QObject::connect(removeRequest, &AliOSSRemoveRequest::uploadError,
				removeRequest, &AliOSSRemoveRequest::deleteLater);

//             TempUploadsTable::remove(path);
		}

        bool FileManager::isTempUploadsUrl(const QString &url)
        {
            auto path = url2path(url);

            // begin with "%tempUploads%/"
            return path.length() > tempUploadsDir.length()
                && path.left(tempUploadsDir.length()) == tempUploadsDir
                && path.at(tempUploadsDir.length()) == '/';
        }

        void FileManager::copyUrlAsync(const QString &urlSource, const QString &pathDest)
        {
			QString srcPath = url2path(urlSource);
			auto copyRequest = adminAdapter.prepareCopy(srcPath, pathDest);

			auto dtstr = AliOSSAdapter::formatDatetime(QDateTime::currentDateTimeUtc());
			auto signature = adminKeyGen.once(
				QStringLiteral("PUT"),
				dtstr,
				srcPath,
                QString(),
                copyRequest->ossHeaders());
			copyRequest->setNeededInformation(dtstr, signature);
			copyRequest->doCopy();

            QObject::connect(copyRequest,
                &AliOSSCopyRequest::uploadFailed,
                [urlSource]()
            {
                qWarning() << "FileManager::copyUrlAsync(" << urlSource << ")failed : " << endl;
            });
			QObject::connect(copyRequest,
                &AliOSSCopyRequest::uploadError,
				[urlSource](QNetworkReply::NetworkError error)
			{
				qWarning() << "FileManager::copyUrlAsync(" << urlSource <<  "), error : " << error << endl;
			});

			// auto-delete
			QObject::connect(copyRequest, &AliOSSCopyRequest::uploadFinished,
				copyRequest, &AliOSSCopyRequest::deleteLater);
			QObject::connect(copyRequest, &AliOSSCopyRequest::uploadFailed,
				copyRequest, &AliOSSCopyRequest::deleteLater);
			QObject::connect(copyRequest, &AliOSSCopyRequest::uploadError,
				copyRequest, &AliOSSCopyRequest::deleteLater);
        }

        void FileManager::moveUrlAsync(const QString &urlSource, const QString &pathDest)
        {
            copyUrlAsync(urlSource, pathDest);
            removeUrlAsync(urlSource);
        }

        QString FileManager::url2path(const QString &url)
		{
			thread_local QRegExp exp = QRegExp(R"(http(s)?:\/\/.*.aliyuncs.com\/(.*))");

			if (-1 != exp.indexIn(url))
			{
				return exp.cap(exp.captureCount());
			}
			throw std::runtime_error("Server.Manager.FileManager.url2path : url is not a vaild url of our server.");
		}

		FileManager* FileManager::instance()
		{
            static FileManager * _instance = new FileManager;
			return _instance;
		}

//         void FileManager::markUsed(const QString &url)
//         {
//             TempUploadsTable::remove(url2path(url));
//         }

	}
}